/**
 * useSyncExternalStore
 *
 *    概念：
 *      让组件可以订阅一个外部的 store 的hook
 *      基于它，可以实现自己的状态管理器
 *      React 18 新增
 *
 *    语法：
 *      const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
 *
 *        subscribe：订阅仓库的函数
 *        getSnapshot：获取store的快照的一个函数
 *        getServerSnapshot： 用于服务端渲染时的一个获取store快照的函数
 *
 *        snapshot： 快照（仓库中的状态数据）
 */

/**
 * 定义一个仓库，与 useSyncExternalStore 配合使用
 */

import { useSyncExternalStore } from "react";

const store = {
  // 仓库中的状态数据
  state: {
    msg: "~",
    count: 0,
  },

  // 仓库的订阅者们
  listeners: [] as any[],

  /**
   * 供组件 useSyncExternalStore 使用的第一个参数
   *
   * listener： 是一个函数，该函数表示订阅者（组件）
   *            该函数的调用，就会通知订阅者仓库数据有更新，需要重新渲染
   *
   * 返回值：    是一个函数，在订阅者（组件）销毁时触发
   */
  subscribe(listener) {
    console.log("subscribe", listener);

    // 将 listener 追加到 listeners 中
    store.listeners.push(listener);

    return () => {
      // 将当前 listener 在 listeners 中移除即可
      store.listeners = store.listeners.filter((cb) => cb !== listener);
    };
  },

  /**
   * 供组件 useSyncExternalStore 使用的第二个参数
   *
   * 返回值：仓库的快照，也是 useSyncExternalStore 的返回值
   */
  getSnapshot() {
    console.log("getSnapshot");

    return store.state;
  },

  /**
   * 供组件 使用的一个派发器，在这里面修改 仓库的 state 数据
   *
   * action：动作，包含有 type 属性的一个对象
   */
  dispatch(action) {
    // 改变仓库自身的数据
    switch (action.type) {
      case "INCREMENT":
        store.state = {
          ...store.state,
          count: store.state.count + 1,
        };
        break;

      case "DECREMENT":
        store.state = {
          ...store.state,
          count: store.state.count - 1,
        };
        break;

      case "changeMsg":
        store.state = {
          ...store.state,
          msg: action.payload || store.state.msg + "~",
        };
        break;

      default:
        break;
    }

    // 要通知订阅者们
    store.listeners.forEach((cb) => {
      cb();
    });
  },
};

function Hello() {
  const state = useSyncExternalStore(store.subscribe, store.getSnapshot);

  return (
    <div>
      <h2>Hello</h2>
      <p>仓库中的数据count: {state.count}</p>
    </div>
  );
}

export default function App() {
  const snapshot = useSyncExternalStore(store.subscribe, store.getSnapshot);

  console.log(`App 渲染, snapshot:`, snapshot);

  return (
    <div>
      <h1>App</h1>
      <button
        onClick={() => {
          store.dispatch({ type: "changeMsg" });
        }}
      >
        {snapshot.msg}
      </button>

      <button
        onClick={() => {
          store.dispatch({ type: "changeMsg", payload: "张三" });
        }}
      >
        修改msg为张三
      </button>

      <button
        onClick={() => {
          store.dispatch({ type: "INCREMENT" });
        }}
      >
        {snapshot.count}
      </button>

      <hr />

      <Hello />
    </div>
  );
}
